/**
 * 
 */
package com.wubo.sec.init;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.Resource;
import org.springframework.orm.hibernate3.HibernateTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.wubo.common.util.ReflectUtils;
import com.wubo.spring.hibernate.BaseHibernateDao;

/**
 * @author WuBo
 * @CreateDate 2011-5-23
 * @version 1.0.1
 */
@SuppressWarnings({"unchecked", "rawtypes"})
public class XmlInitExecutor extends SpringInitExecutor {
	private ApplicationContext ctx = null;
	private String initResourceFile;
	
	public XmlInitExecutor(String initResourceFile){
		this.initResourceFile = initResourceFile;
	}

	@Override
	protected void doInit(ApplicationContext ctx) {
		this.ctx = ctx;
		parseXml();
	}
	
	private void parseXml(){
		InputStream is = null;
		HibernateTransactionManager tx = null;
		TransactionStatus status = null;
		try{
			tx = ctx.getBean("transactionManager", HibernateTransactionManager.class);
			status = tx.getTransaction(new DefaultTransactionDefinition());
			
			BaseHibernateDao dao = ctx.getBean("dao", BaseHibernateDao.class);
			SAXReader xmlReader = new SAXReader();
			xmlReader.setValidation(false);
			
			Resource resource = this.ctx.getResource(initResourceFile);
			
			if(resource == null){
				throw new RuntimeException("Resource "+initResourceFile+" not found.");
			}
			
			is = resource.getInputStream();
			
			Document doc = xmlReader.read(is);
			Element ele = doc.getRootElement();
			
			for(Iterator ite = ele.elementIterator(); ite.hasNext();){
				Element node = (Element)ite.next();
				
				String name = node.getName();
				if("sqls".equals(name)){
					parseSQLNode(node, dao);
				}else if("beans".equals(name)){
					parseBeanNode(node, dao);
				}else{
					throw new RuntimeException("Not a supported node :"+name);
				}
			}
			tx.commit(status);
		}catch(Exception e){
			tx.rollback(status);
			throw new RuntimeException(e);
		}finally{
			if(is != null){
				try{is.close();}catch(Exception e){}
			}
		}
	}
	
	//解析SQL节点
	private void parseSQLNode(Element sqlNode, BaseHibernateDao dao){
		List<Element> sqls = sqlNode.elements();
		for(Element sql : sqls){
			String SQL = sql.getTextTrim();
			dao.executeSQL(SQL);
			System.out.println("*** Execute SQL: "+SQL);
		}
	}
	
	//存放初始化Bean
	private Map<String, Object> initBeanMap;
	//存放非初始化Bean
	private Map<String, Object> notInitBeanMap;
	
	//解析Bean节点
	private void parseBeanNode(Element beanNode, BaseHibernateDao dao) throws Exception {
		List<Element> beans = beanNode.elements();
		
		int beanSize = beans.size();
		
		Map<String, ClassMetaData> beanCmds = new LinkedHashMap<String, ClassMetaData>(beanSize);
		int notInitBeanCount = 0;
		
		for(Element bean : beans){
			List<Attribute> attrs = bean.attributes();
			ClassMetaData cmd = new ClassMetaData();
			for(Attribute attr : attrs){
				cmd.setValue(attr.getName(), attr.getValue());
			}
			if(! cmd.isInit()){
				notInitBeanCount ++;
			}
			beanCmds.put(cmd.getId(), cmd);
		}
		
		initBeanMap = new LinkedHashMap<String, Object>(beanSize - notInitBeanCount);
		notInitBeanMap = new LinkedHashMap<String, Object>(notInitBeanCount);
		
		Collection<ClassMetaData> cmds = beanCmds.values();
		for(Iterator<ClassMetaData> ite=cmds.iterator(); ite.hasNext();){
			createBean(ite.next(), beanCmds);
		}
		
		//持久化Bean
		dao.saveOrUpdateAll(initBeanMap.values());
	}
	/*
	 * 创建Bean并为属性注入值
	 */
	private Object createBean(ClassMetaData cmd, Map<String, ClassMetaData> beanCmds) throws Exception {
		if(initBeanMap.containsKey(cmd.getId())){
			return initBeanMap.get(cmd.getId());
		}
		if(notInitBeanMap.containsKey(cmd.getId())){
			return notInitBeanMap.get(cmd.getId());
		}
		System.out.println("*** Create Bean: "+cmd.getId()+" - "+cmd.getBeanClass().getCanonicalName());
		
		Class beanClass = cmd.getBeanClass();
		Object bean = beanClass.newInstance();
		Hashtable<String, String> fields = cmd.getFields();
		
		for(Iterator<String> fs=fields.keySet().iterator(); fs.hasNext();){
			String fieldName = fs.next();
			String valueString = fields.get(fieldName);
			Class type = ReflectUtils.getField(beanClass, fieldName, true).getType();
			
			if(isValueRefBean(valueString)){
				String beanRef = getValueRef(valueString);
				
				if(isListOrArrayRef(beanRef)){
					System.out.println("*** Reference Array Or List:"+beanRef);
					String[] beanRefs = beanRef.replace("[", "").replace("]", "").split(",");
					List refBeans = new ArrayList(beanRefs.length);
					
					for(String _beanRef : beanRefs){
						System.out.println("*** Array Element:"+_beanRef);
						Object value = createBean(beanCmds.get(_beanRef), beanCmds);
						refBeans.add(value);
					}
					
					if(type.isArray()){
						ReflectUtils.callSetMethod(fieldName, type, bean, refBeans.toArray());
					}else if(Set.class.isAssignableFrom(type)){
						ReflectUtils.callSetMethod(fieldName, type, bean, new java.util.LinkedHashSet(refBeans));
					}else if(List.class.isAssignableFrom(type)){
						ReflectUtils.callSetMethod(fieldName, type, bean, refBeans);
					}else{
						throw new RuntimeException("Not supported type :"+type.getCanonicalName());
					}
				}else{
					System.out.println("*** Reference Bean :"+beanRef);
					Object value = createBean(beanCmds.get(beanRef), beanCmds);
					ReflectUtils.callSetMethod(fieldName, type, bean, value);
				}
			}else{
				Object value = InitUtils.getValue(valueString, type);
				ReflectUtils.callSetMethod(fieldName, type, bean, value);
			}
		}
		if(cmd.isInit()){
			initBeanMap.put(cmd.getId(), bean);
		}else{
			notInitBeanMap.put(cmd.getId(), bean);
		}
		
		return bean;
	}
	//得到引用Bean的Id
	private String getValueRef(String value){
		return value.replace("{", "").replace("}", "");
	}
	//引用类型是否为List或Array
	private boolean isListOrArrayRef(String value){
		return value.matches("\\[.+\\]");
	}
	//是否为引用类型
	private boolean isValueRefBean(String value){
		return value.matches("\\{.+\\}") || value.matches("\\[\\{.+\\}\\]");
	}
	
	private static class ClassMetaData{
		private String id;
		private Class beanClass;
		private boolean init = true;
		private Hashtable<String, String> fields = new Hashtable<String, String>();
		
		public void setValue(String name, String value){
			if("id".equals(name)){
				this.id = value;
			}else if("class".equals(name)){
				try{
					beanClass = Class.forName(value);
				}catch(ClassNotFoundException e){
					throw new RuntimeException(e);
				}
			}else if("init-bean".equals(name)){
				init = Boolean.parseBoolean(value);
			}else{
				fields.put(name, value);
			}
		}
		
		public String getId(){
			return this.id;
		}
		
		public boolean isInit(){
			return this.init;
		}
		
		public Class getBeanClass(){
			return this.beanClass;
		}
		
		public Hashtable<String, String> getFields(){
			return this.fields;
		}
	}
}
